# 用于使用AK协议采集TEOM-1405仪器的数据
# 针对TEOM-1405 ak协议读取上传

# 调用客户端
from thingsboard_gateway.tb_client.tb_device_mqtt import TBDeviceMqttClient
#from tb_device_mqtt import TBDeviceMqttClient 有bug
# 用于自动生成命令行
import fire
import os

# 输出log，便于调试
import logging
basename = os.path.basename (__file__)
basename_without_ext = os.path.splitext(basename)[0]
logger = logging.getLogger(basename_without_ext)
logging.basicConfig(level=logging.INFO)

# 使用时间函数
import time

import serial

# 根据手册
# PRC_code_table={ # 根据需要可以删除一些数据
#     1:'Serial Number',
#     7:'Operating Mode',
#     8:'Status Condition',
#     47:'Current Case Temperature',
#     58:'Current Cap Temperature',
#     61:'Current Ambient Temperature',
#     63:'Current Ambient Relative Humidity',
#     64:'Ambient Dew Point',
#     66:'Current Ambient Pressure',
#     90:'Bypass Flow',
#     96:'Vacuum Pump Pressure',
#     217:'TEOM Flow',
#     231:'TEOM Current Air Temperature',
#     237:'TEOM Filter Loading Percentage',
#     240:'TEOM Mass Concentration',
#     244:'TEOM 1-Hour Average Mass Concentration',
#     245:'TEOM XX-Hour Average Mass Concentration',
#     246:'TEOM 12-Hour Average Mass Concentration',
#     247:'TEOM 24-Hour Average Mass Concentration',
#     248:'TEOM Mass Rate',
#     252:'TEOM Frequency',
#     253:'TEOM Noise'
# }

# 根据实际需要，和对比调整
# 2021-1-1 程迪
# 2021-1-2 chengdi 按修正后的地址更改
PRC_code_table={ # 根据需要可以删除一些数据
    1:'Serial Number',
    7:'Operating Mode',
    8:'Status Condition',
    47:'Current Case Temperature',
    58:'Current Cap Temperature',
    61:'Current Ambient Temperature',
    63:'Current Ambient Relative Humidity',
    64:'Ambient Dew Point',
    66:'Current Ambient Pressure',
    90:'Bypass Flow',
    96:'Vacuum Pump Pressure',
    226:'TEOM Flow',
    231:'TEOM Current Air Temperature',
    237:'Air Tube Temperature',
    245:'TEOM Mass Concentration',
    249:'TEOM 1-Hour Average Mass Concentration',
    248:'TEOM 30-Min Average Mass Concentration',
    242:'filter load',
    251:'TEOM 12-Hour Average Mass Concentration',
    252:'TEOM 24-Hour Average Mass Concentration',
    258:'TEOM Noise'
}
def debug_ak_address(ser):
    for i in range(1,1000):
        ser.write(f"\x024AREG K0 {i}\x03".encode('ascii'))
        response_list = ser.read_until().decode('ascii').split()
        if response_list[2]=='SE\x03':
            logger.info(f'i={i},illegal address')
        else:
            logger.info(f'i={i},value={response_list[3][:-1]}')
    return

def main(HOST='test2.bj-jiashi.com',PORT=10006,TOKEN='19mzDqHnZAXMv0eCtBHr',DELAY=57,
        port = 'com1', baudrate=9600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,timeout=2,
        STATION_NUMBER = 4, CHANNEL_NUMBER='K0', sample_codes=None, log_level="info",debug=False):
    """
    示例：

    """
    # 设置log级别
    logging.basicConfig(level = getattr(logging,log_level.upper(),logging.INFO))

        
    # 建立Thingsboard连接
    TBD = TBDeviceMqttClient(HOST, PORT, TOKEN)
    TBD.connect()
    logger.info('connected!')

    # 建立modbus连接
    ser = serial.Serial(port=port,baudrate=baudrate,bytesize=bytesize,parity=parity,stopbits=stopbits,timeout=timeout)
    
    # debug
    if debug:
        debug_ak_address(ser)
        return
        
    if sample_codes is None:
        sample_codes = PRC_code_table.keys()
    print(f"sample_codes = {sample_codes}")
    print(f"type of sample_codes = {type(sample_codes)}")
    
    retry = 0
    while retry < 5:
        RPC_values={} # 存放返回值
        
        try:
            for sample_code in sample_codes:
                logger.debug(f"sample_code={sample_code}, sample_name={PRC_code_table[sample_code]}")
                request = f'\x02{STATION_NUMBER:1d}AREG {CHANNEL_NUMBER} {sample_code:d}\x03'
                logger.debug(f'request={repr(request)}')
                ser.write(request.encode('ascii'))
                response = ser.read_until().decode('ascii') # read until "/n/r"
                logger.debug(f'response={repr(response)}')
                if (response[2:6]=='????'):
                    logger.warning('command address is unrecognizable')
                elif(response[2:6]=='AREG'):
                    parsed =response[7:-3].split()
                    if len(parsed)==2 and parsed[-1]==b'SE':
                        # error
                        logger.warning('returned error code')
                    else:
                        n_conditions = int(parsed[0])
                        if n_conditions >0 : logger.warning('There is {n_conditions} warning(s)')
                        value_str=parsed[2]
                        if sample_code <= 1: # 序列号是字符串，其他的应该都是数字
                            value = value_str
                        elif sample_code>1 and sample_code <= 10: # 运行模式和状态是整数
                            value = int(value_str)
                        else: #其他应该是浮点数
                            value = float(value_str)
                        RPC_values[sample_code]=value
                        logger.info(f'sample_code={sample_code},{PRC_code_table[sample_code]}={value}')
                else:
                    logger.warning(f'other unknown errors, response={repr(response)}')

            # 按格式准备数据
            mb_data = {PRC_code_table[k]:v for k,v in RPC_values.items()}
            sn = mb_data.pop('Serial Number','Unknown Serial Number')
            logger.info(f'Serial Number is {sn}')
            telemetry = {
                'ts': int(round(time.time() * 1000)),
                'values': mb_data
            }
            logger.info(f'telemetry=\n{telemetry}')
            
            # 发送到thingsboard的MQTT端口
            TBD_info = TBD.send_telemetry(telemetry)
            if TBD_info.rc() != 0:
                logger.warning('发送不成功')

        # 不成功就重试
        except Exception as e:
            logger.warning(e)
            retry = retry +1 
            logger.warning(f'retry={retry}')
            continue   # 重试不用sleep()

        # 间隔
        time.sleep(DELAY)

    # 关闭端口
    ser.close()
    TBD.disconnect()
    
    
# google fire自动生成命令行
if __name__ == "__main__":
    fire.Fire(main)
    # python ak_poll_upload.py --help
    # python ak_poll_upload.py HOST=<other.domain.com> PORT=<other.port>